#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <pthread.h>

#define PORT 8080

int blink_interval = 0; /*存储LED闪烁的间隔时间*/
int stop_blinking = 0; /*控制闪烁是否停止的标志*/
pthread_t blink_thread; /*创建和管理闪烁线程*/

void set_led(int state) { /*设置LED的状态*/
    if (state) {
        printf("LED is ON\n"); /*state 为 1，表示LED开启，打印 "LED is ON"*/
    } else {
        printf("LED is OFF\n"); /*state 为 0，表示LED关闭，打印 "LED is OFF"*/
    }
}

void initial_blink(int interval, int times) { /*控制LED的初始闪烁*/
    for (int i = 0; i < times; i++) {
        set_led(1);
        sleep(interval); /*暂停一段时间*/
        set_led(0);
        sleep(interval);
    }
}

void* blink_led(void* arg) { /*控制 LED 的闪烁,blink_led 函数不接受任何参数并返回一个 void* 类型的指针*/
    while (!stop_blinking) { /*一直执行直到 stop_blinking 变量的值为真（即非零）为止*/
        set_led(1);
        sleep(blink_interval); /*时间长度由 blink_interval 变量指定*/
        set_led(0);
        sleep(blink_interval);
    }
    return NULL;
}

void handle_client(int client_socket) { /*handle_client 的函数，它负责处理客户端发送的消息,参数 client_socket，表示与客户端通信的套接字*/
    char buffer[1024] = {0}; /*定义了一个长度为 1024 的字符数组 buffer，用于存储接收到的消息*/
    while (1) {
        int valread = read(client_socket, buffer, 1024); /*调用 read 函数从 client_socket 套接字中读取数据，并将读取的数据存储到 buffer 中*/
        if (valread > 0) {
            buffer[valread] = '\0'; /*将读取到的数据末尾添加空字符，以确保字符串的结尾*/
            if (strcmp(buffer, "on") == 0) {
                stop_blinking = 1; /*停止 LED 的闪烁*/
                pthread_join(blink_thread, NULL); /*等待闪烁线程结束*/
                set_led(1); /*将 LED 设置为开启状态*/
            } else if (strcmp(buffer, "off") == 0) {
                stop_blinking = 1;
                pthread_join(blink_thread, NULL);
                set_led(0);
            } else if (strncmp(buffer, "blink", 5) == 0) { /*接收到的消息是否以 "blink" 开头*/
                int interval = atoi(buffer + 6); /*使用 atoi 函数将字符串中的数字部分（即间隔时间）转换为整数，并存储在 interval 变量中*/
                /*buffer + 6 表示从字符串的第 7 个字符开始转换，因为 "blink" 长度为 5，后面跟着的是空格和数字*/
                if (interval > 0) {
                    stop_blinking = 1;
                    pthread_join(blink_thread, NULL);
                    blink_interval = interval;/*设置为新的间隔时间*/
                    stop_blinking = 0; /*重新开始 LED 的闪烁*/
                    pthread_create(&blink_thread, NULL, blink_led, NULL); /*创建一个新的闪烁线程，以应用新的间隔时间*/
                } else {
                    printf("Invalid interval received: %s\n", buffer);
                }
            } else {
                printf("Invalid command received: %s\n", buffer);
            }
        } else {
            break;
        }
    }
    close(client_socket);
}

int main() {
    int server_fd, client_socket; /*存储服务器套接字和客户端套接字的文件描述符*/
    struct sockaddr_in address; /*定义了一个 sockaddr_in 结构体变量 address，用于存储服务器地址信息*/
    int opt = 1; /*设置套接字选项*/
    int addrlen = sizeof(address); /*存储 address 结构体的大小*/
        // 开机时 LED 闪烁
    initial_blink(1, 5);  // 每秒闪烁一次，闪烁5次

    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { /*创建一个 TCP 套接字
    socket(AF_INET, SOCK_STREAM, 0)用于创建一个套接字。
    AF_INET表示使用IPv4协议。
    SOCK_STREAM表示使用面向连接的TCP协议。
    第三个参数通常设为0，表示使用默认协议（对于TCP协议，默认就是IP协议）*/
        perror("Socket failed");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        /*设置套接字选项 SO_REUSEADDR 和 SO_REUSEPORT，以便服务器重启后能够立即重新绑定端口*/
        perror("Setsockopt failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET; /*地址族为IPv4*/
    address.sin_addr.s_addr = INADDR_ANY; /*接收任意网卡上的数据，实际上是绑定到本地所有的IP地址*/
    address.sin_port = htons(PORT); /*将端口号从主机字节序转换为网络字节序。PORT是要绑定的端口号*/

    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        /*bind函数把套接字和地址绑定在一起。
         server_fd是之前创建的套接字文件描述符。
        (struct sockaddr *)&address 是一个强制类型转换，把address结构体的地址传递给bind函数*/
        perror("Bind failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    if (listen(server_fd, 3) < 0) { /*设置监听*/
    /*listen函数使套接字进入被动监听状态，准备接收连接请求。
      3是允许的最大连接数的等待队列长度*/
        perror("Listen failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    while (1) {
        if ((client_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
            /*accept等待并接收一个来自客户端的连接
            server_fd是之前创建并绑定好的监听套接字。
            (struct sockaddr *)&address是用来存储客户端地址信息的结构体。
            (socklen_t*)&addrlen是地址结构体的大小。
            client_socket是新的连接套接字，用于与客户端通信*/
            perror("Accept failed");
            close(server_fd);
            exit(EXIT_FAILURE);
        }
        handle_client(client_socket); /*调用handle_client函数来处理客户端的请求，client_socket是刚刚接受的客户端连接套接字*/
    }

    return 0;
}